﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PrototypeGA
{
    class Genetics
    {



        Vector[,,] geneArray;
        double[,] shortestDistanceArray;
        Bus[,] busArray;
        Vector[] mapNodes;
        int maxStops;
        int popSize;
        int numBuses;

        //general constructor
        public Genetics(int popSize, int numBuses, int maxStops, int numNodes)
        {
            geneArray = new Vector[popSize, numBuses, maxStops];
            busArray = new Bus[popSize, numBuses];
            mapNodes = new Vector[maxStops];
            shortestDistanceArray = new double[numNodes, numNodes];
            this.maxStops = maxStops;
            this.popSize = popSize;
            this.numBuses = numBuses;
        }

        public Bus[] getBusArray(int index)
        {
            Bus[] temp = new Bus[busArray.Length];
            for (int i = 0; i < numBuses; i++)
            {
                temp[i] = busArray[index, i];
            }
            return temp;
        }
        //getters/setters
        public void setMap(Vector[] map)
        {
            mapNodes = map;
        }
        public Vector[] getMap(Vector[] map)
        {
            return mapNodes;
        }
        public Vector[, ,] getGenePool()
        {
            return geneArray;
        }
        public void setShortestDistanceArray(double[,] matrix)
        {
            shortestDistanceArray = matrix;
        }

        //MUSTDO inital random pop
        //MUSTDO set the gene array to reference nodes in the nodelist, that way we can go through the node list in one pass
        public void initializeGeneArray()
        {
            Random rand = new Random();

            for (int i = 0; i < popSize; i++)
            {
                for (int j = 0; j < numBuses; j++)
                {
                    geneArray[i,j,0] = mapNodes[0];
                    geneArray[i,j,maxStops - 1] = mapNodes[0];
                    for (int k = 1; k < maxStops-1; k++)
                    {
                        int nextVal = rand.Next()%maxStops;
                        geneArray[i, j, k] = mapNodes[nextVal];
                    }
                }
            }
        }

        //MUSTDO mutation
        public void mutatePopulation()
        {
            Random rand = new Random();
            Random prob = new Random();
            int busNum = 0;
            int nodeNum = 0;
            int nextVal = 0;

            for (int i = 0; i < popSize; i++)
            {
                    int willMutate = prob.Next() % 100;
                    if (willMutate >= 90)
                    {
                        busNum = rand.Next() % numBuses;
                        nodeNum = rand.Next() % maxStops;
                        if (nodeNum != maxStops-1 && nodeNum != 0)
                        {
                            nextVal = rand.Next() % maxStops;
                            geneArray[i, busNum, nodeNum] = mapNodes[nextVal];
                        }
                    }
                
            }

        }

        //MUSTDO test fitness of pop

        public int[] testFitnessAll()
        {
            int[] fitnessArray = new int[popSize];

            //loop through accumulating distances for the buses and getting passengers
            int[] missed = new int[popSize];


            for (int a = 0; a < popSize; a++)
            {
                for (int b = 0; b < numBuses; b++)
                {
                    for (int c = 0; c < maxStops; c++)
                    {
                        geneArray[a, b, c].passengers = geneArray[a,b,c].passengersMax;
                    }
                    busArray[a, b].passengers = 0;
                    busArray[a, b].startTime = false;
                    busArray[a, b].timeOn = 0;
                    busArray[a, b].totalMiles = 0;
                }
            }



            for (int a = 0; a < popSize; a++)
            {
                for (int i = 0; i < maxStops; i++)
                {
                    mapNodes[i].visited = false;
                    mapNodes[i].passengers = mapNodes[i].passengersMax;
                }
                for (int i = 0; i < maxStops-1; i++)
                {
                    for (int z = 0; z < numBuses; z++)
                    {
                        busArray[a, z].passengers += geneArray[a, z, i].passengers;
                        geneArray[a, z, i].passengers = 0;
                        busArray[a,z].totalMiles += shortestDistanceArray[geneArray[a,z,i].id,geneArray[a,z,i+1].id];
                        if (busArray[a, z].startTime == true)
                        {
                            busArray[a, z].timeOn += shortestDistanceArray[geneArray[a, z, i].id, geneArray[a, z, i + 1].id];
                        }
                        if (busArray[a,z].passengers > 0 && busArray[a, z].startTime == false)
                        {
                            busArray[a, z].timeOn += shortestDistanceArray[geneArray[a, z, i].id, geneArray[a, z, i + 1].id];
                            busArray[a, z].startTime = true;
                        }
                        
                        if (mapNodes[geneArray[a, z, i].id].visited == false)
                        {
                            mapNodes[geneArray[a, z, i].id].visited = true;
                            missed[a]++;
                        }
                    }
                }
                missed[a] = maxStops - missed[a];
            
                //MUSTDO calculate total fitness based on bus capacity and total distance traveled
                //accumulating fitness values
                double fitnessValue = 0;
                double milesAccum = 0;
                double totalTime = 0;
                double passengersOver = 0;


                for (int b = 0; b < numBuses; b++)
                {
                    milesAccum += busArray[a, b].totalMiles;
                    totalTime += busArray[a, b].timeOn;
                    if (busArray[a, b].passengers > 40)
                    {
                        passengersOver += busArray[a, b].passengers - 40;

                    }

                }
                fitnessValue = milesAccum*50;
                if (passengersOver > 0)
                {
                    fitnessValue = 100000;
                }
                /*if (totalTime > 100)
                {
                    fitnessValue = 0;
                }*/
                
                //fitnessValue = 1000 / fitnessValue;
                if (missed[a] > 0)
                {
                    fitnessValue = 100000;
                }

                if (fitnessValue == 0)
                {
                    fitnessValue = 1;
                }
                
                fitnessArray[a] = 100000/(int)fitnessValue;

                }
            //check for timeAccum

            return fitnessArray;
        }

        //MUSTDO repopulate based on fitness
        public void repopulatePool(int[] geneFitness)
        {
            int index = geneFitness.Length;
            int temp = 0;
            for (int r = 0; r < geneFitness.Length; r++)
            {
                temp += geneFitness[r];
            }
            int next = 0;
            Vector[,,] geneWheel = new Vector[(int)temp, numBuses, maxStops];
            for (int i = 0; i < index; i++)
            {
                for (int j = 0; j < geneFitness[i]; j++)
                {
                    for (int a = 0; a < numBuses; a++)
                    {
                        for (int z = 0; z < maxStops; z++)
                        {
                            geneWheel[next, a, z] = geneArray[i,a, z];
                        }
                        
                    }
                    next++;
                }
            }

            //repopulate
            int nextInd = -1;
            int mate = -1;
            int crossoverChance = -1;
            int crossoverLocation = -1;
            int crossoverBusNum = -1;

            Random rand = new Random();
            for (int q = 0; q < index; q++)
            {
                nextInd = rand.Next() % (int)temp;
                crossoverChance = rand.Next() % 100;
                if (crossoverChance < 95)
                {
                    for (int d = 0; d < numBuses; d++)
                    {
                        for (int r = 0; r < maxStops; r++)
                        {

                            geneArray[q, d, r] = geneWheel[nextInd, d, r];
                        }
                    }
                }
                else
                {
                    mate = rand.Next() % (int)temp;
                    crossoverBusNum = rand.Next() % 2;
                    crossoverLocation = (rand.Next() % (maxStops-2)) + 1;
                    for (int d = 0; d < numBuses; d++)
                    {
                        if (crossoverBusNum != d)
                        {
                            for (int r = 0; r < maxStops; r++)
                            {
                                geneArray[q, d, r] = geneWheel[nextInd, d, r];
                            }
                        }
                        else
                        {
                            for (int r = 0; r < crossoverLocation; r++)
                            {
                                geneArray[q, d, r] = geneWheel[mate, d, r];
                            }
                            for (int r = crossoverLocation; r < maxStops; r++)
                            {
                                geneArray[q, d, r] = geneWheel[mate, d, r];
                            }
                        }
                    }
                }
            }
        }


        //struct for buses
        public struct Bus
        {
            public int passengers;
            
            public double totalMiles;
            public Boolean startTime;
            public double timeOn;
            public Bus(int fail)
            {
                passengers = 0;
                
                totalMiles = 0;
                timeOn = 0;
                startTime = false;
            }
        }

        //struct for vectors
        

    }
    public class Vector
    {
        public int passengers;
        public int passengersMax;
        public int id;
        public Boolean visited;
        public int x;
        public int y;

        public Vector(int passengers, int id, int x, int y)
        {
            this.passengers = passengers;
            passengersMax = passengers;
            this.id = id;
            visited = false;
            this.x = x;
            this.y = y;
            
        }

    }
}
